﻿#include "controller.h"
#include "commonfunction.h"
#include <QDebug>
#include <QThread>
#include <QMetaType>
#include <QMessageBox>
#include "log4z.h"
#include "userlogger.h"
#include "errordef.h"
#include "systempreset.h"

#define HOST_REPLY_EOF '\u0006'
#define HOST_ERROR_CODE(commCode) ERROR_CODE(commCode)
#define HOST_ERROR_MSG(commCode) ERROR_MSG(commCode)
#define HOST_ERROR_TIP(commCode) ERROR_TIP(commCode)

using namespace zsummer::log4z;

Controller::Controller(QObject *parent) : QSerialPort(parent), commOpened(false), isCheckPass(false)
{
    progressVal = 0;
    progressTimer = new QTimer(this);
    progressTimer->setInterval(20);

    commTimeOutTimer = new QTimer(this);
    commTimeOutTimer->setInterval(1000);

    commRecvTimer = new QTimer(this);
    commRecvTimer->setInterval(100);

    connect(this, &Controller::readyRead, this, &Controller::readSerialPortSlot);
    connect(progressTimer, &QTimer::timeout, this, &Controller::updateProgressSlot);

    connect(commTimeOutTimer, &QTimer::timeout, this, &Controller::updatecommTimeOutSlot);
    connect(commRecvTimer, &QTimer::timeout, this, &Controller::updatecommRecvSlot);

    progressCount = SystemPreset::instance()->getIntData(EnumPresetItem::SelfTestMaxTimes) * 1000;

    selftestReply.cmd = "DETECT";
    selftestReply.type = DETECT;
}

Controller::~Controller()
{
    if (progressTimer->isActive())
    {
        progressTimer->stop();
        progressTimer->deleteLater();
    }

    if (commTimeOutTimer->isActive())
    {
        commTimeOutTimer->stop();
        commTimeOutTimer->deleteLater();
    }

    if (commRecvTimer->isActive())
    {
        commRecvTimer->stop();
        commRecvTimer->deleteLater();
    }
}

QString Controller::make_communicate_packet(CMD_TYPE cmd_type, int value1, int value2, int value3, int value4, int value5)
{
    QString retString = "";
    this->xcmd_type = cmd_type;
    switch(cmd_type)
    {
    case DETECT:
        retString = QString("DETECT");
        break;
    case HP2PAR:
        retString = QString("HP2PAR%1,%2,%3,%4,%5").arg(value5).arg(value1).arg(value2).arg(value3).arg(value4);
        break;
    case SHOCHK:
        retString = QString("SHOCHK");
        break;
    case HP2STR :
        retString = QString("HP2STR");
        break;
    case HP2STP:
        retString = QString("HP2STP");
        break;
    case RDBSTR:
        retString = QString("RDBSTR");
        break;
    case RDBSTP:
        retString = QString("RDBSTP");
        break;
    case SHAOPN:
        retString = QString("SHAOPN");
        break;
    case SHASTP:
        retString = QString("SHASTP");
        break;
    case PWRCHK:
        retString = QString("PWRCHK%1,%2,%3").arg(value1).arg(value2).arg(value3);
        break;
    case FLWCHK:
        retString = QString("FLWCHK%1,%2").arg(value1).arg(value2);
        break;
    case PWROFF:
        retString = QString("PWROFF");
        break;
    case LQDVOL:
        retString = QString("LQDVOL%1").arg(value1);
        break;
    case VERNUM:
        retString = QString("VERNUM");
        break;
    case ROTCAL:
        retString = QString("ROTCAL");
        break;
    case SHACAL:
        retString = QString("SHACAL");
        break;
    case LEDCTL:
        retString = QString("LEDCTL%1").arg((char)value1);
        break;
    case XZDJSZ:
        retString = QString("XZDJSZ");
        break;
    case XZDJSN:
        retString = QString("XZDJSN");
        break;
    case HP3STR:
        retString = QString("HP3STR");
    default:
        break;
    }

    return retString;
}

void Controller::decodeBuffer(QString buffer)
{
    qDebug() << "decodeBuffer:  " << buffer << endl;

    if (0 == QString::compare(buffer.mid(0, 6), "DETECT", Qt::CaseSensitive))
    {
        qDebug() << "decodeBuffer: " << buffer;

        selftestReply.parameter = QString(buffer.mid(6, 2));

        if (0 == QString::compare(buffer.mid(6, 2), "00", Qt::CaseSensitive))
        {
            selftestReply.message = tr("Detect OK");;
            isCheckPass = true;

            // send version check
            ComCmd cmd;
            cmd.type = CMD_TYPE::VERNUM;
            sendPortCmdSlot(cmd);
        }
        else
        {
            QString errorCode = buffer.mid(0, 8);
            QString info = HOST_ERROR_MSG(errorCode);
            QString tips = HOST_ERROR_TIP(errorCode);
            selftestReply.message = info;
            selftestReply.tips = tips;
            emit selfCheckResultFailSignal(selftestReply);
            progressTimer->stop();
        }
    }
    else if (0 == QString::compare(buffer.mid(0, 6), "PWRCHK", Qt::CaseSensitive))
    {
        QString errorCode = buffer.mid(0, 8);
        QString info = HOST_ERROR_MSG(errorCode);
        QString tips = HOST_ERROR_TIP(errorCode);

        HostReply reply = {buffer.mid(0, 6), PWRCHK, info, tips, buffer.mid(6, 1).toInt()};
        emit pwdflwCheckResultSignal(reply);
    }
    else if (0 == QString::compare(buffer.mid(0, 6), "FLWCHK", Qt::CaseSensitive))
    {
        QString errorCode = buffer.mid(0, 8);
        QString info = HOST_ERROR_MSG(errorCode);
        QString tips = HOST_ERROR_TIP(errorCode);

        HostReply reply = {buffer.mid(0, 6), FLWCHK, info, tips, buffer.mid(6, 1).toInt()};
        emit pwdflwCheckResultSignal(reply);
    }
    else if (
             //检查参数
             (0 == QString::compare(buffer.mid(0, 6), "HP2PAR", Qt::CaseSensitive)) ||
             (0 == QString::compare(buffer.mid(0, 6), "SHOCHK", Qt::CaseSensitive)) ||

             //打开蠕动泵和摇动
             (0 == QString::compare(buffer.mid(0, 6), "SHAOPN", Qt::CaseSensitive)) ||
             (0 == QString::compare(buffer.mid(0, 6), "RDBSTR", Qt::CaseSensitive)) ||

             //电击开始
             (0 == QString::compare(buffer.mid(0, 6), "HP2STR", Qt::CaseSensitive)) ||
             //关闭开始
             (0 == QString::compare(buffer.mid(0, 6), "HP2STP", Qt::CaseSensitive)) ||

             //继续实验
             (0 == QString::compare(buffer.mid(0, 6), "HP3STR", Qt::CaseSensitive)) ||

             //关闭蠕动泵和摇动
             (0 == QString::compare(buffer.mid(0, 6), "RDBSTP", Qt::CaseSensitive)) ||
             (0 == QString::compare(buffer.mid(0, 6), "SHASTP", Qt::CaseSensitive))

             )
    {
        ComCmd cmd;

        if (0 == QString::compare(buffer.mid(0, 6), "HP2PAR", Qt::CaseSensitive))
        {
            cmd.type = CMD_TYPE::SHOCHK;
            sendPortCmdSlot(cmd);
            qDebug() << "recv HP2PAR " << endl;
        }
        else if (0 == QString::compare(buffer.mid(0, 6), "SHOCHK", Qt::CaseSensitive))
        {
            QString errorCode = buffer.mid(0, 8);
            QString info = HOST_ERROR_MSG(errorCode);
            QString tips = HOST_ERROR_TIP(errorCode);

            qDebug("%s<ACK>: %s\n", errorCode.toStdString().data(), info.toStdString().data());
            HostReply reply = {errorCode, SHOCHK, info, tips, buffer.mid(6, 2).toInt()};
            emit shkCheckResultSignal(reply);
        }

        //电击开始
        else if (0 == QString::compare(buffer.mid(0, 6), "HP2STR", Qt::CaseSensitive))
        {
            QString errorCode = buffer.mid(0, 8);
            if (0 == QString::compare(buffer.mid(6, 2), "10", Qt::CaseSensitive))
            {
                QString info = tr("Shock Started");
                qDebug() << errorCode + "<ACK>:" << info << endl;
                HostReply reply = {errorCode, HP2STR, info, "", buffer.mid(9).toInt()};
                emit experimentStartSignal(reply);
            }
            else if (0 == QString::compare(buffer.mid(6, 2), "11", Qt::CaseSensitive))
            {
                QString info = tr("Shock Finished");
                qDebug() << errorCode + "<ACK>:" << info << endl;
                HostReply reply = {errorCode, HP2STR, info, "", asciiListToInt(buffer.mid(10, 8))};
                emit expResultPassSignal(reply);
            }
            else if (0 == QString::compare(buffer.mid(6, 2), "07", Qt::CaseSensitive))
            {
                QString info = tr("Liquid level sensor detected liquid");
                qDebug() << errorCode + "<ACK>:" << info << endl;
                emit liquidDetectedSignal();
            }
            else
            {
                QString info = HOST_ERROR_MSG(errorCode);
                QString tips = HOST_ERROR_TIP(errorCode);

                qDebug("%s<ACK>: %s\n", errorCode.toStdString().data(), info.toStdString().data());
                HostReply reply = {errorCode, HP2STR, info, tips, buffer.mid(6, 2)};
                emit expResultFailSignal(reply);
            }
        }

        else if (0 == QString::compare(buffer.mid(0, 6), "HP3STR", Qt::CaseSensitive))
        {
            QString errorCode = buffer.mid(0, 8);
            if (0 == QString::compare(buffer.mid(6, 2), "10", Qt::CaseSensitive))
            {// 已开始
                QString info = tr("Shock Started");
                qDebug() << errorCode + "<ACK>:" << info << endl;
                HostReply reply = {errorCode, HP3STR, info, "", buffer.mid(9).toInt()};
                emit experimentStartSignal(reply);
            }
            else if (0 == QString::compare(buffer.mid(6, 2), "11", Qt::CaseSensitive))
            {// 已完成
                QString info = tr("Shock Finished");
                qDebug() << errorCode + "<ACK>:" << info << endl;
                HostReply reply = {errorCode, HP3STR, info, "", asciiListToInt(buffer.mid(10, 8))};
                emit expResultPassSignal(reply);
            }
        }

        //关闭蠕动泵和摇动
        else if (0 == QString::compare(buffer.mid(0, 6), "HP2STP", Qt::CaseSensitive))
        {
            HostReply reply = {buffer.mid(0, 7), HP2STP, "", "", buffer.mid(6, 1).toInt()};
            if (0 == QString::compare(buffer.mid(6, 1), "1", Qt::CaseSensitive))
            {
                reply.message = tr("Shock Stopped");
            }
            else
            {
                reply.message = tr("Shock Failed");
            }

            emit shkCheckResultSignal(reply);
        }
    }
    else if (0 == QString::compare(buffer.mid(0, 6), "VERNUM", Qt::CaseSensitive))
    {
        //VERNUMV1.0.0_20211223<ACK>
        SHK_Result ret;
        ret.type = CMD_TYPE::VERNUM;
        ret.ret = 0;
        ret.info = buffer.mid(6, 15);

        qDebug() << "host Version: " << ret.info;
        emit hostVersionSignal(ret.info);
    }
    else if (0 == QString::compare(buffer.mid(0, 6), "LEDCTL", Qt::CaseSensitive))
    {
        HostReply host = {};
        host.cmd = buffer.mid(0, 7);
        host.type = CMD_TYPE::LEDCTL;
        host.message = tr("Led Control Done.");
        host.parameter = buffer.mid(6, 1);
    }
    else if (0 == QString::compare(buffer.mid(0, 6), "XZDJSZ", Qt::CaseSensitive))
    {
        HostReply host = {};
        host.cmd = buffer.mid(0, 7);
        host.type = CMD_TYPE::XZDJSZ;
        host.message = tr("Motor Calibration Done.");
        host.parameter = buffer.mid(6, 1);
        emit motorCalibDoneSignal(host);
    }
    else if (buffer.startsWith("LIQUID"))
    {// 液位传感器测试日志
    }
    else
    {// 记录未解析的指令
        Q_ASSERT_X(false, "decodeBuffer()", buffer.toStdString().data());
    }
}

void Controller::openSerialPortSlot(const QString &name)
{
    setPortName(name);
    if (open(QIODevice::ReadWrite))
    {
        commOpened = true;
        setBaudRate(QSerialPort::Baud57600);
        setDataBits(QSerialPort::Data8);
        setParity(QSerialPort::NoParity);
        setStopBits(QSerialPort::OneStop);
        setFlowControl(QSerialPort::NoFlowControl);
        LOGFMTD("Func: %s : %s", __FUNCTION__, "Serial Port Open Success");
    }
    else
    {
        commOpened = false;
        LOGFMTD("Func: %s : %s", __FUNCTION__, "Serial Port Open Failed");
        qDebug() << "-----port open failed--------------";
    }

    UserLogger::debug(QString("%1: %2 %3").arg(tr("Serial Port")).arg(name).arg(commOpened ? tr("Open Success") : tr("Open Failed")));
    qDebug() << "openSerialPort end---------------------";
}

void Controller::sendPortCmdSlot(ComCmd cmd)
{
    if (!commOpened)
    {
        qDebug("Error: Serial Port not Connected");
        return;
    }

    char head[2] = {0x01, 0x02};
    char tail[1] = {0x03};
    QString str =  make_communicate_packet(cmd.type,cmd.value1,cmd.value2,cmd.value3,cmd.value4,cmd.value5);
    QByteArray ba = str.toLatin1(); // must
    LOGFMTD("%s(): %s", __FUNCTION__, ba.data());   //str.toStdString().c_str()
    mutex.lock();
    write(head, 2);
    write(ba);
    write(tail, 1);
    mutex.unlock();
    //Open comm TimerOut timer
    if (cmd.type != PWROFF)
    {
        EnablecommTimeOut();
    }

    // send command log
    QThread::msleep(10);
    UserLogger::debug("<-" + str);
}

void Controller::POST()
{
    //Detect
    progressTimer->start();
    ComCmd cmd;
    cmd.type = CMD_TYPE::DETECT;
    cmd.value1 = 0;
    cmd.value2 = 0;
    cmd.value3 = 0;
    cmd.value4 = 0;
    sendPortCmdSlot(cmd);
}

void Controller::watchPower()
{
    qDebug() << "Controller::watchPower()" << "thread:" << QThread::currentThreadId();
}

void Controller::updatecommRecvSlot()
{
    if (commRecvTimer->isActive())
    {
        commRecvTimer->stop();
        DisablecommTimeOut();
        LOGFMTD("%s(): %s", __FUNCTION__, byteBuffRecv.data());

        QByteArray noParseBytes;
        bool lastCmdCompleted = byteBuffRecv.endsWith(HOST_REPLY_EOF);
        QList<QByteArray> cmds = byteBuffRecv.split(HOST_REPLY_EOF);
        int cmdLength = lastCmdCompleted ? cmds.count() - 1: cmds.count();
        for (int i = 0; i < cmdLength; i++)
        {// 处理粘包
            if (!lastCmdCompleted && (cmdLength - 1 == i))
            {// 记录最后一条不完整的命令
                noParseBytes = cmds.at(i);
                qDebug() << "noParseBytes:" << noParseBytes;
            }
            else
            {// 处理单条命令
                emit dataReceivedSignal(cmds.at(i));
                decodeBuffer(cmds.at(i));
            }
        }

        byteBuffRecv.clear();
        byteBuffRecv.prepend(noParseBytes);
    }
}

void Controller::lightLedSlot(const LedType &led)
{
    ComCmd cmd;
    cmd.type = LEDCTL;
    cmd.value1 = (int)led;
    sendPortCmdSlot(cmd);
}

int Controller::asciiListToInt(const QString &asciiList)
{
    int sum = 0;
    for (int i = 0; i < asciiList.length(); i++)
    {
        sum = (sum << 4) + (asciiList.at(i).toLatin1() - '0');
    }

    return sum;
}

void Controller::readSerialPortSlot()
{
    QByteArray byteBuff = readAll();
    byteBuffRecv.append(byteBuff);
    if(!commRecvTimer->isActive())
    {
        commRecvTimer->start();
    }
}

void Controller::EnablecommTimeOut()
{
    commTimeOutVal = 0;
    commTimeOutTimer->start();
}

void Controller::DisablecommTimeOut()
{
    commTimeOutTimer->stop();
    commTimeOutVal = 0;
}


void Controller::updatecommTimeOutSlot()
{
    commTimeOutVal ++;
    if (commTimeOutVal > 60)
    {
        DisablecommTimeOut();
        LOGFMTD("Func: %s: cmd timeout 60s no response", __FUNCTION__);
        //emit commTimeOutSignal(progressVal);
    }
}

void Controller::updateProgressSlot()
{
    progressVal += isCheckPass ? 150 : 20;
    emit progressChangeSignal(progressVal);
    if (!commOpened)
    {
        progressTimer->stop();
        selftestReply.parameter = 0xEE;
        selftestReply.message = tr("Host Connection Failed");
        emit selfCheckResultFailSignal(selftestReply);
        return;
    }

    if (progressVal >= progressCount)
    {
        progressTimer->stop();
        if (isCheckPass)
        {
            emit selfCheckResultPassSignal(selftestReply);
        }
        else
        {
            selftestReply.parameter = 0xFF;
            selftestReply.message = tr("Detect timeout");
            emit selfCheckResultFailSignal(selftestReply);
        }
    }
}
